﻿#region Using

using System.Diagnostics;

using Common.Base;
using Common.Enums;
using Common.Extensions;

using GameCommon.Args;
using GameCommon.ManagerInterfaces;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

#endregion

namespace GameCommon.Manager
{
    internal class MouseManager : IMouseManager
    {
        private ButtonState leftMouseButtonState;

        private ButtonState rightMouseButtonState;

        private Position mousePressedPositionLeft;

        private Position mouseReleasedPositionLeft;

        private Position mousePressedPositionRight;

        private Position mouseReleasedPositionRight;
        
        public MouseManager()
        {
            this.leftMouseButtonState = ButtonState.Released;
            this.rightMouseButtonState = ButtonState.Released;
        }

        public event MouseButtonEventHandler MouseButtonPressed;

        public event MouseButtonEventHandler MouseButtonReleased;

        public event MouseMovedEventHandler MouseMoved;

        public event MouseDraggedLeftEventHandler MouseDraggedLeft;

        public event MouseDraggedRightEventHandler MouseDraggedRight;

        public Position MousePosition { get; private set; }

        public void Update(GameTime gameTime)
        {
            var state = Mouse.GetState();

            this.EvaluateMousePosition(state);
            this.EvaluteLeftMouseButton(state);
            this.EvaluteRightMouseButton(state);
            this.EvaluateMouseDraggedLeft();
            this.EvaluateMouseDraggedRight();
        }

        private void EvaluateMouseDraggedLeft()
        {
            if (this.mousePressedPositionLeft == null || this.mouseReleasedPositionLeft == null)
            {
                return;
            }

            if (!this.mousePressedPositionLeft.Equals(this.mouseReleasedPositionLeft) && this.mousePressedPositionLeft.Distance(this.mouseReleasedPositionLeft) > 5)
            {
                Debug.WriteLine("MouseManager: Mouse dragged Left");
                this.OnMouseDraggedLeft(this.mousePressedPositionLeft, this.mouseReleasedPositionLeft);
                this.mousePressedPositionLeft = null;
                this.mouseReleasedPositionLeft = null;
            }
        }

        private void EvaluateMouseDraggedRight()
        {
            if (this.mousePressedPositionRight == null || this.mouseReleasedPositionRight == null)
            {
                return;
            }

            if (this.mousePressedPositionRight.Distance(this.mouseReleasedPositionRight) > 5)
            {
                Debug.WriteLine("MouseManager: Mouse dragged Right");
                this.OnMouseDraggedRight(this.mousePressedPositionRight, this.mouseReleasedPositionRight);
                this.mousePressedPositionRight = null;
                this.mouseReleasedPositionRight = null;
            }
        }
        
        private void EvaluteLeftMouseButton(MouseState state)
        {
            if (state.LeftButton == ButtonState.Pressed && this.leftMouseButtonState == ButtonState.Released)
            {
                Debug.WriteLine("MouseManager: LeftMouseButton pressed");
                this.OnMouseButtonPressed(MouseButtonEnum.Left);
                this.leftMouseButtonState = state.LeftButton;
                this.mousePressedPositionLeft = new Position(state.X, state.Y);
                this.mouseReleasedPositionLeft = null;
                return;
            }

            if (state.LeftButton == ButtonState.Released && this.leftMouseButtonState == ButtonState.Pressed)
            {
                Debug.WriteLine("MouseManager: LeftMouseButton released");
                this.OnMouseButtonReleased(MouseButtonEnum.Left);
                this.leftMouseButtonState = state.LeftButton;
                this.mouseReleasedPositionLeft = new Position(state.X, state.Y);
            }
        }

        private void EvaluteRightMouseButton(MouseState state)
        {
            if (state.RightButton == ButtonState.Pressed && this.rightMouseButtonState == ButtonState.Released)
            {
                Debug.WriteLine("MouseManager: RightMouseButton pressed");
                this.OnMouseButtonPressed(MouseButtonEnum.Right);
                this.rightMouseButtonState = state.RightButton;
                this.mousePressedPositionRight = new Position(state.X, state.Y);
                this.mouseReleasedPositionRight = null;
                return;
            }

            if (state.RightButton == ButtonState.Released && this.rightMouseButtonState == ButtonState.Pressed)
            {
                Debug.WriteLine("MouseManager: RightMouseButton released");
                this.OnMouseButtonReleased(MouseButtonEnum.Right);
                this.rightMouseButtonState = state.RightButton;
                this.mouseReleasedPositionRight = new Position(state.X, state.Y);
            }
        }

        private void EvaluateMousePosition(MouseState state)
        {
            if (this.MousePosition == null)
            {
                this.MousePosition = new Position(state.X, state.Y);
                return;
            }

            if (this.MousePosition.X != state.X || this.MousePosition.Y != state.Y)
            {
                this.MousePosition = new Position(state.X, state.Y);
                this.OnMouseMoved();
            }
        }

        private void OnMouseDraggedLeft(Position mousePressed, Position mouseReleased)
        {
            var args = new MouseDraggedArgs(mousePressed, mouseReleased);
            
            if (this.MouseDraggedLeft != null)
            {
                this.MouseDraggedLeft(args);
            }
        }

        private void OnMouseDraggedRight(Position mousePressed, Position mouseReleased)
        {
            var args = new MouseDraggedArgs(mousePressed, mouseReleased);
            
            if (this.MouseDraggedRight != null)
            {
                this.MouseDraggedRight(args);
            }
        }

        private void OnMouseButtonReleased(MouseButtonEnum button)
        {
            var args = new MouseButtonArgs(button, this.MousePosition);

            if (this.MouseButtonReleased != null)
            {
                this.MouseButtonReleased(args);
            }
        }

        private void OnMouseButtonPressed(MouseButtonEnum button)
        {
            var args = new MouseButtonArgs(button, this.MousePosition);

            if (this.MouseButtonPressed != null)
            {
                this.MouseButtonPressed(args);
            }
        }

        private void OnMouseMoved()
        {
            var args = new MouseMovedArgs { Position = this.MousePosition };

            if (this.MouseMoved != null)
            {
                this.MouseMoved(args);
            }
        }
    }
}